package gupao.concurrency.threadpool;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class StarvationSample {

    /*
        static final BlockingQueue<Runnable> threadPoolTaskQueue = new LinkedBlockingDeque<Runnable>(){
            @Override
            public Runnable take() throws InterruptedException {
                return super.takeLast();
            }

            @Override
            public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException {
                return super.pollLast(timeout, unit);
            }

            @Override
            public Runnable poll() {
                return super.pollLast();
            }
        };
    */
    static final BlockingQueue<Runnable> threadPoolTaskQueue = new PriorityBlockingQueue<>();

    @AllArgsConstructor
    static class PriorityTask implements Runnable{
        int priority;
        @Override
        public void run() {
            log.info("task(priority={}) run.", priority);
            Utils.sleepIgnoreExp(1200);
        }
    }

    static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1,
            TimeUnit.MINUTES, threadPoolTaskQueue){

        @Override
        protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
            return new PriorityFutureTask<T>(runnable, value);
        }

        class PriorityFutureTask<T> extends FutureTask<T> implements Comparable<PriorityFutureTask<T>>{
            PriorityTask task;
            public PriorityFutureTask(Runnable runnable, T result) {
                super(runnable, result);
                if(runnable instanceof  PriorityTask){
                    task = (PriorityTask)runnable;
                }
            }

            @Override
            public int compareTo(PriorityFutureTask<T> o) {
                if(task == null || o.task == null){
                    return 0;
                }
                return Integer.compare(o.task.priority, task.priority);
            }
        }
    };
    static volatile boolean starvationTaskFinished = false;
    /*static final Runnable starvationTask = () -> {
        log.info("starvation task run.");
        Utils.sleepIgnoreExp(200);
        starvationTaskFinished = true;
    };*/
    static final PriorityTask starvationTask = new PriorityTask(0);

    public static void main(String[] args) {
        threadPoolExecutor.submit(() -> {
            log.info("the first task is running.");
            Utils.sleepIgnoreExp(3000);
        });
        threadPoolExecutor.submit(starvationTask);
        log.info("add task(priority={})",starvationTask.priority);
        addRegularTasks();
        while (!starvationTaskFinished) {
            Utils.sleepIgnoreExp(10);
        }
    }

    public static void addRegularTasks() {
        for (int i = 0; i < 20; i++) {
            String name = "rq-thread-" + i;
            /*threadPoolExecutor.submit(() -> {
                log.info("regular task {} is running.", name);
                Utils.sleepIgnoreExp(1000);
            });*/
            threadPoolExecutor.submit(new PriorityTask(5));
            log.info("add task(priority=5)");
            Utils.sleepIgnoreExp(1000);
        }
    }
}
